#include <linux/sched.h>
#include <linux/hrtimer.h>
#include <linux/export.h>

/*
 * Sleep related functions:
 */
static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer)
{
    struct hrtimer_sleeper *t =
        container_of(timer, struct hrtimer_sleeper, timer);
    struct task_struct *task = t->task;

    t->task = NULL;
    if (task)
        wake_up_process(task);

    return HRTIMER_NORESTART;
}

/**
 * hrtimer_sleeper_start_expires - Start a hrtimer sleeper timer
 * @sl:		sleeper to be started
 * @mode:	timer mode abs/rel
 *
 * Wrapper around hrtimer_start_expires() for hrtimer_sleeper based timers
 * to allow PREEMPT_RT to tweak the delivery mode (soft/hardirq context)
 */
void hrtimer_sleeper_start_expires(struct hrtimer_sleeper *sl,
                                   enum hrtimer_mode mode)
{
    /*
     * Make the enqueue delivery mode check work on RT. If the sleeper
     * was initialized for hard interrupt delivery, force the mode bit.
     * This is a special case for hrtimer_sleepers because
     * __hrtimer_setup_sleeper() determines the delivery mode on RT so the
     * fiddling with this decision is avoided at the call sites.
     */
    if (IS_ENABLED(CONFIG_PREEMPT_RT) && sl->timer.is_hard)
        mode |= HRTIMER_MODE_HARD;

    hrtimer_start_expires(&sl->timer, mode);
}
EXPORT_SYMBOL_GPL(hrtimer_sleeper_start_expires);

static void __hrtimer_setup_sleeper(struct hrtimer_sleeper *sl,
                                    clockid_t clock_id, enum hrtimer_mode mode)
{

    hrtimer_setup(&sl->timer, hrtimer_wakeup, clock_id, mode);
    sl->task = current;
}

/**
 * hrtimer_setup_sleeper_on_stack - initialize a sleeper in stack memory
 * @sl:		sleeper to be initialized
 * @clock_id:	the clock to be used
 * @mode:	timer mode abs/rel
 */
void hrtimer_setup_sleeper_on_stack(struct hrtimer_sleeper *sl,
                                    clockid_t clock_id, enum hrtimer_mode mode)
{
    __hrtimer_setup_sleeper(sl, clock_id, mode);
}
